Flutter异常搜集和上报

您所在的位置:网站首页 flutter 日志 Flutter异常搜集和上报

Flutter异常搜集和上报

2024-02-02 06:35| 来源: 网络整理| 查看: 265

简介

Flutter中的异常虽然不像Native那样会直接导致app crash,但也是不容忽视的. 比如widget构建失败,又或是某个网络请求解析失败,所以针对flutter我们也需要有一套规则来捕捉异常,下面主要是介绍异常类型, 全局异常捕捉的三种方式、异常报告的几种形式, 在查看了Isolate,Future,FlutterError.onError,相关的代码实践出来的.

Flutter中常见的异常

Flutter中最最常见的就是空指针异常了,关于可选类型这块始终是Flutter这门语言的痛点之一,总之Flutter在数据结构转换这块和可选类型和移动端语言还是有很大差距的,希望官方快点优化吧。

字典转换,类型推倒,文件读取,网络请求错误,布局溢出,数组越界等,插件通信异常等等,通常用Error和Exception来描述

1.Error: 用于定义程序执行错误的对象

Error (dart.core) AsyncError (dart.async) JsonUnsupportedObjectError (dart.convert) JsonCyclicError (dart.convert) LateInitializationErrorImpl (dart._internal) FlutterError (assertions.dart) RemoteError (dart.isolate) UnderflowError (quiver.async) MatchError (quiver.testing.equality) FallThroughError (dart.core) CastError (dart.core) UnsupportedError (dart.core) UnimplementedError (dart.core) ConcurrentModificationError (dart.core) LateInitializationError (dart.core) LateInitializationErrorImpl (dart._internal) OutOfMemoryError (dart.core) AbstractClassInstantiationError (dart.core) NoSuchMethodError (dart.core) TypeError (dart.core) UnimplementedError (dart.core) NullThrownError (dart.core) AssertionError (dart.core) FlutterError (assertions.dart) StackOverflowError (dart.core) CyclicInitializationError (dart.core) StateError (dart.core) ArgumentError (dart.core) IndexError (dart.core) RangeError (dart.core)

2.Exception: 由dartVM和自定义的dart代码手动抛出

Exception (dart.core) DeferredLoadException (dart.async) TimeoutException (dart.async) IsolateSpawnException (dart.isolate) IOException (dart.io) HttpException (dart._http) WebSocketException (dart._http) FileSystemException (dart.io) ProcessException (dart.io) SignalException (dart.io) TlsException (dart.io) SocketException (dart.io) StdoutException (dart.io) StdinException (dart.io) PlatformException (message_codec.dart) MissingPluginException (message_codec.dart) TickerCanceled (ticker.dart) NetworkImageLoadException (image_provider.dart) PathException (path_exception.dart) UsageException (usage_exception.dart) SourceSpanException (span_exception.dart) MultiSourceSpanException (span_exception.dart) SourceSpanFormatException (span_exception.dart) ImageException (image_exception.dart) ParserException (petitparser.core.contexts.exception) XmlException (xml.utils.exceptions) XmlNodeTypeException (xml.utils.exceptions) XmlParserException (xml.utils.exceptions) XmlParentException (xml.utils.exceptions) XmlTagException (xml.utils.exceptions) ClosedException (closed_exception.dart) RemoteException (remote_exception.dart) _RemoteTestFailure (remote_exception.dart) FormatException (dart.core) ArchiveException (archive_exception.dart) ArgParserException (arg_parser_exception.dart) MultiSourceSpanFormatException (span_exception.dart) SourceSpanFormatException (span_exception.dart) XmlParserException (xml.utils.exceptions) IntegerDivisionByZeroException (dart.core) _Exception (dart.core)

Error和Exception他们都代表了异常,但是从命名上来看似乎Error级别的日志更倾向于程序执行错误,不可预知的问题,如dart vm内部抛出的异常,数据严重级别较高的异常,甚至会让程序直接瘫痪; Exception更多的则是开发者自定义的异常,预知到的问题,并做相应的try catch去处理这种case.

所以这就要求我们在程序开发时要有较强的安全意思,合理利用Exception定义函数可能出现的异常,并做好相应的try catch和备注

对于Error类型的异常,要提前做好校验,如TypeError,确保代码的准确性,对于Future和Stream这类的异步操作如果使用了await关键字,一定要使用try catch,否则它将会直接block后面的执行的代码,尤其是在程序启动阶段需要格外注意。

Flutter中的异常捕捉

通过阅读flutter framework层的源代码不难发现捕获异常主要由Isolate和Zone,还有Flutter.error,此外基于Zone封装而来的Future和Stream都可以进行异常捕捉.因为他们的依赖关系如下:

Stream -> Future -> Zone -> ParentZone -> Isolate, Stream作为最小的单位,如果错误发生在Stream类,我们可以手动hook住优先拦截,如果不做处理将会层层传递, Stream和Future主要用于业务逻辑的编写,我们可以根据业务场景选择捕获和忽略,如果是有await这类的关键字,则一定要使用异常捕捉,不然它会直接抛出一行到当前的zone,会block住后面代码的执行.

在启动时注册Isolate和currentZone,和FlutterError.onError事件能够获取到app所有的异常了,同时记录当前的异常堆栈。

此外还可以进一步将异常堆栈数据发送至后台,或者是发送邮件到开发者,当然也可以直接通过设置后门开关,将异常堆栈的信息显示的展示在widget上。

Flutter error捕捉 它主要侧重于Flutter框架层的异常输出,如widget构建,图片读取,RenderObject绘制,点击事件的分发,测试框架,统计分析。相关的类如下: dev/benchmarks/macrobenchmarks/lib/src/web/recorder.dart: packages/flutter/lib/src/foundation/assertions.dart: packages/flutter/lib/src/gestures/binding.dart: packages/flutter/lib/src/gestures/pointer_router.dart: packages/flutter/lib/src/painting/image_provider.dart: packages/flutter/lib/src/widgets/fade_in_image.dart: packages/flutter/lib/src/widgets/framework.dart: packages/flutter/lib/src/widgets/image.dart: packages/flutter/lib/src/widgets/widget_inspector.dart: packages/flutter/test/rendering/rendering_tester.dart: packages/flutter_test/lib/src/binding.dart: 使用方式 FlutterError.onError = (FlutterErrorDetails details) async { _reportError(details.exception, details.stack, errorDetails: details); }; Zone和Isolate的异常捕捉 开辟一个新的Zone来捕捉,这种方案有一些局限性,它只能捕捉到Zone内部的异常,由于Zone的异常会逐级上抛给parent,所以我们可以利用这个特点,将程序的根Widget加入到当前的Zone,这样就能捕获到所有遗漏的异常的。 runZonedGuarded(() async { if (ensureInitialized) { WidgetsFlutterBinding.ensureInitialized(); } runApp(rootWidget); }, (dynamic error, StackTrace stackTrace) { _reportError(error, stackTrace); }); Isolate为flutter的提供了独立的进程空间,程序运行也是依赖于Isolate的创建,所有的函数包括Zone.run都是在这个对应的Isolate下运行,此外系统提供了Isolate的几个钩子函数,方便我们拦截对应的回掉事件,通过注册ErrorListener就能实现全局错误的监听, Isolate.current.addErrorListener(new RawReceivePort((dynamic pair) async { var isolateError = pair as List; _reportError( isolateError.first.toString(), isolateError.last.toString(), ); }).sendPort); 处理异常数据

打印的堆栈信息可以发送到指定的后台服务用于测试 还可以以邮件的形式发送,便于问题排查 另外Flutter提供了ErrorWidget的构造方法,它主要有以下2个使用场景,局限于Widget的build,在实际应用中,我们可以保存一个全局的GlobalKey用来存储context,这样就能在其他出现的异常部分也能创建错误的Widget并显示在界面上了。

packages/flutter/lib/src/widgets/layout_builder.dart: 102 } catch (e, stack) { 103: built = ErrorWidget.builder( 104 _debugReportException( 118 } catch (e, stack) { 119: built = ErrorWidget.builder( 120 _debugReportException( packages/flutter/lib/src/widgets/sliver.dart: 1628 FlutterError.reportError(details); 1629: return ErrorWidget.builder(details); 总结

1、任何时候都不要对于类型的非空判定,只要不是百分百不为空就必须得预处理,设置默认值或者添加可选操作符号,如 optialValue?.property或 optialValue ?? 0

2、合理的定义Expection定义可能出现的异常并捕获,比如解析,类型推导,缓存读取,例如

try{ final userInfo = await servie.getUserInfo(); } on NetExpection catch (e, string){ ... }

3、定义全局的异常捕捉

FlutterError.onError = (FlutterErrorDetails details) async { ... }; FlutterError.onError = (FlutterErrorDetails details) async { _reportError(details.exception, details.stack, errorDetails: details); }; Isolate.current.addErrorListener(new RawReceivePort((dynamic pair) async { ... }).sendPort); runZonedGuarded(() async { if (ensureInitialized) { WidgetsFlutterBinding.ensureInitialized(); } runApp(rootWidget); }, (dynamic error, StackTrace stackTrace) { ... });

通过**GlobalKey**获取到当前的context,将错误信息通过widget输出到屏幕上;在实际使用的时候会比较频繁,建议设置个后门开关,选择性的弹出;继续扩展,发送邮件调用api,发送至后台;至此,一个简易的 bugReport就搜集完成了,如果不追求排版格式几十行代码就搞定了.

Future和Zone相关的异常捕获测试 class CustomException implements Exception { CustomException({String message}); } /** - 当Future在构造时注册了OnError事件,将会拦截其他的所有onError事件 result: start end onError Instance of 'CustomException' `StackTrace.current` */ void testFutureOnErrorHasMostPriority(){ print('start'); runZonedGuarded((){ final future = Future.error(CustomException(message: 'CustomException')); future.then((value){ print('then value $value'); },onError: (e,s){ //最高优先级 print('onError $e `StackTrace.current`'); } ).catchError((e, s){ print('catchError $e `StackTrace.current`'); return null; }, test: (object) { if (object is CustomException) { print('test object $object true'); //当为true时这是一个可恢复的错误,交由`catchError`继续处理 return true; } else { print('test object $object false');//当为false时不可恢复错误,交友 `runZonedGuarded`的`onError`处理 return false; } }); },(e,s){ print('runZonedGuarded onError: $e $s'); }); print('end'); } /** - 当testObjet接收到Error时可以校验,如果确定不是error,返回true,可以由`catchError`转换成替换值 start end test object Instance of 'CustomException' true catchError Instance of 'CustomException' `StackTrace.current` Exited */ void testFutureOnErrorDelegateToSelfTestAndCatch(){ print('start'); runZonedGuarded((){ final future = Future.error(CustomException()); future.then((value){ print('then value $value'); }).catchError((e, s){ print('catchError $e `StackTrace.current`'); return null; }, test: (object) { if (object is CustomException) { print('test object $object false'); return false; } else { print('test object $object false'); return false; } }); },(e,s){ print('runZonedGuarded onError: $e $s'); }); print('end'); } /** - Future处理不了的异常抛给`Zone` start end runZonedGuarded onError: Instance of 'CustomException' Exited */ void testFutureOnErrorDelegateToParentZoneIfUnCatch(){ print('start'); runZonedGuarded((){ final future = Future.error(CustomException()); future.then((value){ print('then value $value'); }); },(e,s){ print('runZonedGuarded onError: $e $s'); }); print('end'); }

更多学习资讯请关注我的Gitee



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3